{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<center>\n",
    "<img src=\"../../img/ods_stickers.jpg\">\n",
    "## Открытый курс по машинному обучению\n",
    "<center>Автор материала: Григорьев Георгий Глебович (@thepowerfuldeez)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# <center>Уменьшение размерности с TSNE и сравнение скорости работы c UMAP</center>\n",
    "## <center>В этом туториале будут рассмотрены библиотеки TSNE, Multicore TSNE и UMAP и будет произведено сравнение на датасете 20_newsgroups</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "Всем привет! В нашем курсе частично поднималась тема Manifold Learning, но для большей ясности думаю стоит еще раз вспомнить про эту область машинного обучения и немного глубже затронуть ее аспекты.\n",
    "\n",
    "> Manifold Learning – метод нелинейного уменьшения размерности, при котором обычно размерность настолько большая, что интерпретировать данные становится сложно.\n",
    "\n",
    "К таким алгоритмам относятся t-SNE и uMAP, а к алгоритмам линейного уменьшения размерности – PCA, SVD.\n",
    "Часто размерность уменьшают до 2 или 3, чтобы данные целиком можно было бы отобразить на двумерное или трехмерное пространство.\n",
    "\n",
    "Тут надо подметить, что если размерность экстремально большая и исчисляется десятками тысяч, то нужно использовать более эффективные методы уменьшения размерности, например линейные PCA и SVD (но никто не запрещает использовать сначала PCA, а потом TSNE)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[tl;dr](#4.-Выводы)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1. Подготовка"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Импортируем нужные библиотеки"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T13:00:00.288923Z",
     "start_time": "2018-04-21T13:00:00.242463Z"
    }
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "\n",
    "sns.set()\n",
    "%matplotlib inline\n",
    "import os\n",
    "\n",
    "from sklearn.decomposition import TruncatedSVD\n",
    "from sklearn.manifold import TSNE"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для установки Multicore TSNE может понадобиться cmake и gcc\n",
    "\n",
    "На Windows склонировать репозиторий куда угодно и запустить pip install на эту директорию\n",
    "\n",
    "Возможно придется заменить pip на pip3, если запускаете не из virtualenv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T10:48:10.287351Z",
     "start_time": "2018-04-21T10:47:57.550058Z"
    }
   },
   "outputs": [],
   "source": [
    "!git clone https://github.com/DmitryUlyanov/Multicore-TSNE.git && pip install Multicore-TSNE/ && rm -rf Multicore-TSNE/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T11:20:25.440895Z",
     "start_time": "2018-04-21T11:20:25.435209Z"
    }
   },
   "outputs": [],
   "source": [
    "from MulticoreTSNE import MulticoreTSNE"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Установим uMAP, для установки может потребоваться установить numba вручную, но все должно встать автоматически"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T10:49:06.567754Z",
     "start_time": "2018-04-21T10:48:53.223290Z"
    }
   },
   "outputs": [],
   "source": [
    "!pip install umap-learn\n",
    "# так же, возможно придется заменить на pip3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T11:20:36.143925Z",
     "start_time": "2018-04-21T11:20:36.140558Z"
    }
   },
   "outputs": [],
   "source": [
    "from umap import UMAP"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Загрузим первый датасет 20_newsgroups"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T10:43:00.941096Z",
     "start_time": "2018-04-21T10:43:00.937672Z"
    }
   },
   "outputs": [],
   "source": [
    "# from sklearn.datasets import fetch_20newsgroups\n",
    "# newsgroups = fetch_20newsgroups('all')\n",
    "# Можно было бы их скачать таким образом, но из-за блокировки серверов AWS РКН ссылка не работает"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Поэтому я нашел оригинальную ссылку, на Windows придется [скачать](http://qwone.com/~jason/20Newsgroups/20news-18828.tar.gz) и распаковать вручную и закинуть папку 20news-18828 в /data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T11:04:47.885169Z",
     "start_time": "2018-04-21T11:04:13.512070Z"
    }
   },
   "outputs": [],
   "source": [
    "!wget -O ../../data/20news-18828.tar.gz http://qwone.com/~jason/20Newsgroups/20news-18828.tar.gz && tar -xzf ../../data/20news-18828.tar.gz -C ../../data && rm ../../data/20news-18828.tar.gz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Текст нужно препроцессить, используем TfIdfVectorizer со следующими параметрами:\n",
    "\n",
    " - ``input='filename'``: использует напрямую пути к файлам, векторизатор умеет их читать сам.    \n",
    "\n",
    "\n",
    " - ``decode_error='ignore'``: пропускать проблемные участки текстов (не все из них закодированы удачно, т.к. мы скачали датасет напрямую, а проводить тщательный анализ текстов не входит в цель данного урока) Мы увидим, что так мы не пропустим ни один файл.\n",
    "\n",
    "\n",
    " - ``max_features=100000``: Для наглядности ограничим размерность X 100000, вообще полная размерность была бы 170000"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T13:06:57.795849Z",
     "start_time": "2018-04-21T13:06:57.722525Z"
    }
   },
   "outputs": [],
   "source": [
    "from sklearn.feature_extraction.text import TfidfVectorizer\n",
    "\n",
    "# названия классов = названия директорий, которые содержат файлы с текстами\n",
    "labels = [a for a in os.listdir(\"../../data/20news-18828/\") if a != \".DS_Store\"]\n",
    "\n",
    "X = []\n",
    "y = []\n",
    "for label in labels:\n",
    "    base_path = f\"../../data/20news-18828/{label}\"\n",
    "    filepaths = os.listdir(base_path)\n",
    "    X.extend([os.path.join(base_path, filepath) for filepath in filepaths])\n",
    "    y.extend([label] * len(filepaths))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T13:07:11.986595Z",
     "start_time": "2018-04-21T13:06:59.441705Z"
    }
   },
   "outputs": [],
   "source": [
    "X_trans = TfidfVectorizer(input='filename', decode_error='ignore', max_features=100000).fit_transform(X)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T13:07:57.184107Z",
     "start_time": "2018-04-21T13:07:57.180361Z"
    }
   },
   "outputs": [],
   "source": [
    "# Проверяем, что не потеряли никаких файлов\n",
    "assert len(X) == X_trans.shape[0] == len(y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-22T00:09:00.937095Z",
     "start_time": "2018-04-22T00:09:00.931555Z"
    }
   },
   "outputs": [],
   "source": [
    "X_trans.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Наша исходная размерность – 100000 и 20 классов, данные разреженые"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T23:54:07.120199Z",
     "start_time": "2018-04-21T23:54:07.097188Z"
    }
   },
   "outputs": [],
   "source": [
    "# цвета для каждого класса – будет использоваться на графике\n",
    "from matplotlib.cm import get_cmap\n",
    "\n",
    "cm = get_cmap(\"gist_rainbow\")\n",
    "unique_y = list(np.unique(y))\n",
    "N_COLORS = len(unique_y)\n",
    "colors = dict(zip(np.unique(y), [cm(unique_y.index(l) / N_COLORS) for l in unique_y]))\n",
    "# для отображения легенды\n",
    "import matplotlib.patches as mpatches\n",
    "\n",
    "patches = [mpatches.Patch(color=colors[l], label=l) for l in unique_y]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Сначала попробуем просто отобразить данные на плоскость с помощью линейных методов.\n",
    "\n",
    "Может весь туториал зря и можно просто было сделать по-простому?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-22T00:27:54.513173Z",
     "start_time": "2018-04-22T00:27:53.551723Z"
    }
   },
   "outputs": [],
   "source": [
    "X_svd_2 = TruncatedSVD(2).fit_transform(X_trans)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-22T00:28:05.377454Z",
     "start_time": "2018-04-22T00:28:02.956627Z"
    }
   },
   "outputs": [],
   "source": [
    "plt.figure(figsize(18, 14))\n",
    "plt.scatter(X_svd_2[:, 0], X_svd_2[:, 1], s=3, c=[colors[l] for l in y])\n",
    "plt.legend(handles=patches)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Не зря. Зато выглядит симпатично."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2. TSNE"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Казалось бы: ну все, теперь мы преодбработали данные, имеем вектора, размерность большая: закинем все в TSNE и получим красивую визуализацию, но нет.\n",
    "\n",
    "Как было написано в начале, нелинейные методы не способны обработать экстремально большие размерности. t-SNE уменьшает размерность данных опираясь на локальные свойства, поэтому он не сработает на данных с очень большой размерностью.\n",
    "\n",
    "Решение – использовать линейное уменьшение размерности и уже на нем прогонять все остальные алгоритмы."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T23:27:24.413075Z",
     "start_time": "2018-04-21T23:27:11.342567Z"
    }
   },
   "outputs": [],
   "source": [
    "# Для sparse данных идеально подойдет TruncatedSVD\n",
    "X_svd = TruncatedSVD(100).fit_transform(X_trans)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Теперь мы готовы к уменьшению размерности"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Рассмотрим некоторые параметры, которые есть у TSNE:\n",
    "\n",
    " -  ``n_components``: Результирующая размерность. TSNE устроен так, что результаты \n",
    "    будут плохо интерпретироваться при значениях больше 3 (а некоторые реализации и\n",
    "    вовсе не поддерживают эти значения) Поэтому TSNE используется больше для визуализации.\n",
    "    \n",
    "По умолчанию – 2\n",
    "\n",
    " -  ``perplexity``: Количество соседних точек, будет определять качество \n",
    "    представления данных. Большие датасеты требуют большого значения, \n",
    "    Рекомендуется ставить значение от 5 до 50, но TSNE не чуствителен к этому параметру.\n",
    "    \n",
    "По умолчанию – 30\n",
    "\n",
    " -  ``metric``: Выбор метрики для подсчета расстояния между точками. Они есть [здесь](https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.pdist.html).\n",
    "\n",
    "По умолчанию - euclidean"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "У нашей \"быстрой\" реализации TSNE есть параметр n_jobs, который сильно выручает, ставим его -1 (использовать все ядра).\n",
    "\n",
    "P.S. Было решено даже не использовать обычную версию TSNE из sklearn, предполагается, что читателю известно, что выполнение не завершится за приемлемое время, так что я даже решил не ждать."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T14:44:49.763237Z",
     "start_time": "2018-04-21T14:41:21.110793Z"
    }
   },
   "outputs": [],
   "source": [
    "%%time\n",
    "X_tsne = MulticoreTSNE(n_jobs=-1).fit_transform(X_svd)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T23:54:32.559807Z",
     "start_time": "2018-04-21T23:54:29.624235Z"
    }
   },
   "outputs": [],
   "source": [
    "plt.figure(figsize(18, 14))\n",
    "plt.scatter(X_tsne[:, 0], X_tsne[:, 1], s=3, c=[colors[l] for l in y])\n",
    "plt.legend(handles=patches)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Видно, что евклидова метрика (по умолчанию) справляется плохо. Для векторов, получаемых из текста обычно используется косинусная мера."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T14:30:23.361300Z",
     "start_time": "2018-04-21T14:26:54.487344Z"
    }
   },
   "outputs": [],
   "source": [
    "%%time\n",
    "X_tsne_cosine = MulticoreTSNE(metric='cosine', n_jobs=-1).fit_transform(X_svd)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T23:54:56.307529Z",
     "start_time": "2018-04-21T23:54:53.133822Z"
    }
   },
   "outputs": [],
   "source": [
    "plt.figure(figsize(18, 14))\n",
    "plt.scatter(X_tsne_cosine[:, 0], X_tsne_cosine[:, 1], s=3, c=[colors[l] for l in y])\n",
    "plt.legend(handles=patches)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T14:50:50.232648Z",
     "start_time": "2018-04-21T14:50:50.221639Z"
    }
   },
   "source": [
    "Уже лучше, хоть в центре все равно не разобрать происходящее"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Попробуем увеличить perplexity "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T23:31:46.632458Z",
     "start_time": "2018-04-21T23:27:59.420335Z"
    }
   },
   "outputs": [],
   "source": [
    "%%time\n",
    "X_tsne_cosine_perplexity = MulticoreTSNE(perplexity=40, metric='cosine', n_jobs=-1).fit_transform(X_svd)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T23:55:13.679996Z",
     "start_time": "2018-04-21T23:55:10.682713Z"
    }
   },
   "outputs": [],
   "source": [
    "plt.figure(figsize(18, 14))\n",
    "plt.scatter(X_tsne_cosine_perplexity[:, 0], X_tsne_cosine_perplexity[:, 1], s=3, c=[colors[l] for l in y])\n",
    "plt.legend(handles=patches)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Попробуем уменьшить perplexity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T23:38:42.732517Z",
     "start_time": "2018-04-21T23:33:34.700196Z"
    }
   },
   "outputs": [],
   "source": [
    "%%time\n",
    "X_tsne_cosine_perplexity2 = MulticoreTSNE(perplexity=12, metric='cosine', n_jobs=-1).fit_transform(X_svd)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T23:55:25.587612Z",
     "start_time": "2018-04-21T23:55:22.553517Z"
    }
   },
   "outputs": [],
   "source": [
    "plt.figure(figsize(18, 14))\n",
    "plt.scatter(X_tsne_cosine_perplexity2[:, 0], X_tsne_cosine_perplexity2[:, 1], s=3, c=[colors[l] for l in y])\n",
    "plt.legend(handles=patches)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Мы убедились, что параметр perplexity по большому счету ни на что не влияет и TSNE почти не конфигурируется"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3. UMAP"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Рассмотрим параметры, которые есть у uMAP:\n",
    "\n",
    " -  ``n_neighbors``: Количество соседних точек, будет определять качество \n",
    "    представления данных. Большое значение повлечет за собой более глобальную структуру, \n",
    "    потеряются особенности данных в небольшой окрестности. Рекомендуется ставить значение\n",
    "    от 5 до 50, подстраивая под свои нужды. Стандартно от 10 до 15.\n",
    "\n",
    " -  ``min_dist``: Минимальная дистанция между точками в итоговом представлении.\n",
    "    Большие значения более равномерно распределят точки, когда как маленькие\n",
    "    восстановят зависимости в локальной окрестности. Рекомендуется изменять от\n",
    "    0.001 до 0.5. Стандартно – 0.1.\n",
    "\n",
    " -  ``metric``: Выбор метрики для подсчета расстояния между точками. Можно задать свою\n",
    "    функцию, предварительно оформив ее декоратором numba.jit"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T23:20:49.344370Z",
     "start_time": "2018-04-21T23:20:25.983129Z"
    }
   },
   "outputs": [],
   "source": [
    "%%time\n",
    "X_umap = UMAP(metric='cosine').fit_transform(X_svd)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Уже сейчас заметно ускорение в ~10 раз!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T23:55:45.507442Z",
     "start_time": "2018-04-21T23:55:43.259627Z"
    }
   },
   "outputs": [],
   "source": [
    "plt.figure(figsize(18, 14))\n",
    "plt.scatter(X_umap[:, 0], X_umap[:, 1], s=3, c=[colors[l] for l in y])\n",
    "plt.legend(handles=patches)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Выглядит тоже довольно симпатично, но вот какие-то отдаленные точки уж очень напоминают аутлаеры, а внутри наоборот все довольно плотно.\n",
    "Поставим минимальную дистанцию в 2 раза больше и кол-во соседей поменьше"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T23:39:02.571376Z",
     "start_time": "2018-04-21T23:38:45.335546Z"
    }
   },
   "outputs": [],
   "source": [
    "%%time\n",
    "X_umap_tuned = UMAP(n_neighbors=8, min_dist=0.2, metric='cosine').fit_transform(X_svd)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T23:53:48.465138Z",
     "start_time": "2018-04-21T23:53:45.691379Z"
    }
   },
   "outputs": [],
   "source": [
    "plt.figure(figsize(18, 14))\n",
    "plt.scatter(X_umap_tuned[:, 0], X_umap_tuned[:, 1], s=3, c=[colors[l] for l in y])\n",
    "plt.legend(handles=patches)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Вот такое представление данных мне нравится больше! Но что если данные в центре просто не способны отобразиться на плоскость? \n",
    "\n",
    "Отобразим на 3-мерное пространство"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-21T23:42:46.182973Z",
     "start_time": "2018-04-21T23:42:28.192410Z"
    }
   },
   "outputs": [],
   "source": [
    "%%time\n",
    "X_umap_tuned_3d = UMAP(n_components=3, n_neighbors=8, min_dist=0.2, metric='cosine').fit_transform(X_svd)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-22T00:17:04.939762Z",
     "start_time": "2018-04-22T00:17:01.523276Z"
    }
   },
   "outputs": [],
   "source": [
    "from mpl_toolkits.mplot3d import Axes3D\n",
    "\n",
    "ax = plt.subplot(111, projection='3d')\n",
    "plt.figure(figsize(18, 14))\n",
    "ax.scatter(X_umap_tuned_3d[:, 0], X_umap_tuned_3d[:, 1], X_umap_tuned_3d[:, 2], s=3, c=[colors[l] for l in y])\n",
    "ax.set_zlim3d(-5, 4)\n",
    "ax.set_ylim3d(-5, 5)\n",
    "ax.set_xlim3d(-4, 4)\n",
    "ax.legend(handles=patches, loc='upper left', numpoints=1, ncol=3, fontsize=8, bbox_to_anchor=(0, 0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Теперь, когда мы построили неплохое распределение мы можем обучить логистическую регрессию на исходных (не уменьшенных) векторах и попытаться сопоставить те, которые были плохо выделены в кластер с их итоговым скором."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-22T00:13:33.353627Z",
     "start_time": "2018-04-22T00:13:33.349544Z"
    }
   },
   "outputs": [],
   "source": [
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.metrics import classification_report\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.preprocessing import LabelEncoder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-22T00:13:29.851357Z",
     "start_time": "2018-04-22T00:13:19.374105Z"
    }
   },
   "outputs": [],
   "source": [
    "X_train, X_test, y_train, y_test = train_test_split(X_trans, LabelEncoder().fit_transform(y))\n",
    "y_predicted = LogisticRegression().fit(X_train, y_train).predict(X_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-04-22T00:18:46.818970Z",
     "start_time": "2018-04-22T00:18:46.811968Z"
    }
   },
   "outputs": [],
   "source": [
    "print(classification_report(y_test, y_predicted, target_names=unique_y))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Все точки из категории 'rec' (зеленого цвета) хорошо выделены в кластеры, это подтверждается большим скором в таблице\n",
    "\n",
    "'talk.religion.misc' определяется плохо, оно и понятно, ведь это категория \"прочее\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4. Выводы\n",
    "\n",
    "Мы еще раз за время курса попробовали уменьшить размерность данных и визуализировать данные, познакомились с библиотекой UMAP, которая (на мой взгляд) строит более совершенные визуализации с возможностью настройки, да еще и намного быстрее."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
